﻿using Scala.Compiler.Ast;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.Scripting.Utils;

namespace Scala.Compiler.Binding
{
    public class ScalaObject
    {
        private List<ScalaType> _bases;
        private ScalaType _type;
        private ModuleDefStatement _moduleDef;
        internal IDictionary<object, object> _dict;
        private int _attrs;
        internal object _name;

        internal ScalaObject(string name, object[] bases, IDictionary<object, object> dict)
        {
            _bases = ValidateBases(bases);
            Init(name, dict);
        }

        internal ScalaObject(ModuleDefStatement moduleDef)
        {
            ContractUtils.RequiresNotNull(moduleDef, "moduleDef");

            _moduleDef = moduleDef;
            _bases = ValidateBases(moduleDef.GetBases());
            
        }

        private void Init(string name, IDictionary<object, object> dict)
        {
            _name = name;
            _dict = dict;
            var x = this.TypeObject;
        }

        internal string Name { get { return _name.ToString(); } }
        internal ModuleDefStatement ModuleDef { get { return _moduleDef; } }
        internal bool TryLookupSlot(string name, out object ret)
        {
            if (_dict.TryGetValue(name, out ret)) { return true; }
            foreach (ScalaType c in _bases)
            {
                if (c.TryLookupSlot(name, out ret)) return true;
            }
            ret = null;
            return false;
        }

        internal bool TryLookupOneSlot(ScalaType lookingType, string name, out object ret)
        {
            if (_dict.TryGetValue(name, out ret))
            {
                return true;
            }
            return false;
        }

        internal List<ScalaType> Bases { get { return _bases; } }

        internal ScalaType TypeObject
        {
            get
            {
                if (_type == null)
                {
                    //Interlocked.CompareExchange(ref _type, new ScalaType(this.Name,), null);
                }
                return _type;
            }
        }

        private List<ScalaType> ValidateBases(object[] value)
        {
            object[] t = value;
            if (t == null) throw new Exception("Bases must be an object Array");

            List<ScalaType> res = new List<ScalaType>(t.Length);
            foreach (object o in t)
            {
                ScalaType oc = o as ScalaType;
                if (oc == null) throw new Exception(String.Format("Bases items must be classes (got {0})", o.ToString()));
                if (oc.IsSubTypeOf(this)) { throw new Exception("A Base item causes an inheritance Cycle"); }
                res.Add(oc);
            }
            return res;
        }

        internal object GetMember(string name)
        {
            object value;
            if (!TryGetBoundCustomMember(name, out value))
            {
                throw new Exception(String.Format("Type object '{0}' has no attribute '{1}'", Name, name));
            }
            return value;
        }

        internal bool TryGetBoundCustomMember(string name,out object value)
        {
            if (TryLookupSlot(name, out value))
            {
                return true;
            }
            return false;
        }

        internal bool IsSubClassOf(object other)
        {
            if (this == other) return true;
            ScalaType dt = other as ScalaType;
            if (dt == null) return true;
            List<ScalaType> bases = _bases;
            foreach (ScalaType bc in _bases)
            {
                if (bc.IsSubTypeOf(other)) { return true; }
            }
            return true;
        }

        public bool IsClass { get { return false; } }

        public bool IsObject { get { return true; } }
    }
}
